using System;
using System.Collections.Generic;
using System.Speech.Synthesis;
using System.Threading;
using System.Text;


namespace SophiaBot
{
    public class VoiceBot
    {
        #region local fields

        protected SpeechSynthesizer _synthesizer = null;
        protected string _selectedVoice = string.Empty;
        protected List<string> availableVoices = new List<string>();
        protected bool _isSpeechOn = false;
        protected object asyncVoiceLock = new object();
        protected string _name = string.Empty;
        protected string _previousVoiceName = string.Empty;

        public event GenericEventHandler<string> Write;

        #endregion

        #region constructor
        public VoiceBot()
        {
            //text to speech code
            _synthesizer = new SpeechSynthesizer();
            _synthesizer.SetOutputToDefaultAudioDevice();
            LoadVoiceList();
        }

        #endregion

        #region helper methods

        /// <summary>
        /// Loads the voice list.
        /// </summary>
        protected void LoadVoiceList()
        {
            foreach (InstalledVoice voice in _synthesizer.GetInstalledVoices())
            {
                availableVoices.Add(voice.VoiceInfo.Name);
            }
            if (availableVoices.Count > 0)
                SelectedVoice = availableVoices[0];
        }

        /// <summary>
        /// Parses the name of the selected synthesizer voice.
        /// </summary>
        /// <value>The parsed name of the selected synthesizer voice.</value>
        protected string ParseVoiceName
        {
            get
            {
                if (SelectedVoice != string.Empty)
                {
                    string result = string.Empty;
                    string voiceName = SelectedVoice.Replace("Microsoft", "").Replace("LH", "").Trim();
                    if (voiceName != _previousVoiceName)
                        result = _name + " ( in " + voiceName + "'s voice)";
                    else
                        result = _name;
                    _previousVoiceName = voiceName;
                    return result;
                }
                else
                {
                    return _name;
                }
            }
        }

        /// <summary>
        /// Speaks the text.
        /// </summary>
        /// <param name="output">The output.</param>
        protected void SpeakText(string output)
        {
            if (_isSpeechOn)
            {
                BotServerBase.DebugWriteToConsole("beginning synthetic vocalization");
                _synthesizer.SelectVoice(SelectedVoice);
                _synthesizer.SpeakAsync(output);
            }
        }


        #endregion

        #region public methods


        /// <summary>
        /// Gets or sets a value indicating whether this instance is speech on.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if this instance is speech on; otherwise, <c>false</c>.
        /// </value>
        public bool IsSpeechOn
        {
            get { return _isSpeechOn; }
            set { _isSpeechOn = value; }
        }

        /// <summary>
        /// Gets the state.
        /// </summary>
        /// <value>The state.</value>
        public SynthesizerState State
        {
            get { return _synthesizer.State; }
        }

        /// <summary>
        /// Gets the voice list.
        /// </summary>
        /// <value>The voice list.</value>
        public string[] VoiceList
        {
            get { return availableVoices.ToArray(); }
        }

        /// <summary>
        /// Gets or sets the selected voice.
        /// </summary>
        /// <value>The selected voice.</value>
        public string SelectedVoice
        {
            get { return _selectedVoice; }
            set { _selectedVoice = value; }
        }

        /// <summary>
        /// Gets or sets the name.
        /// </summary>
        /// <value>The name.</value>
        public string Name
        {
            get { return _name; }
            set { _name = value; }
        }

        /// <summary>
        /// Sends text to the client slowly, imitating a slow typist.
        /// </summary>
        /// <param name="outputText">The output text.</param>
        public void TypeSlow(string outputText)
        {
            if (null == Write)
                return;
            //synchronize with asynch speech synthesis
            //so method doesn't complete until
            //both text and synthesis are complete
            BotServerBase.DebugWriteToConsole("try lock asyncVoiceLock (type slow)");
            lock (asyncVoiceLock)
            {
                BotServerBase.DebugWriteToConsole(" asyncVoiceLock locked (type slow)");
                try
                {
                    Thread.Sleep(500);
                    Write(ParseVoiceName + ": ");
                    Thread.Sleep(1000);
                    SpeakText(outputText);

                    for (int i = 0; i < outputText.Length; i++)
                    {
                        Write(outputText.Substring(i, 1));           
                        Thread.Sleep(50);
                    }
                    BotServerBase.DebugWriteToConsole("write: " + outputText);
                    Write(Environment.NewLine + Environment.NewLine);

                    while (_synthesizer.State == SynthesizerState.Speaking)
                    {
                        Thread.Sleep(100);
                    }
                    BotServerBase.DebugWriteToConsole("End write slow");
                }
                catch
                {
                    _synthesizer.SpeakAsyncCancel(new Prompt(outputText));
                    Write("The SAPIBot parser encountered an error.");
                }
                BotServerBase.DebugWriteToConsole(" asyncVoiceLock released (type slow)");
            }
        }


        /// <summary>
        /// Sends text to the client instantaneously.
        /// </summary>
        /// <param name="outputText">The output text.</param>
        public void TypeFast(string outputText)
        {
            if (null == Write)
                return;

            try
            {
                SpeakText(outputText);
                Write(ParseVoiceName + ": " + outputText + Environment.NewLine + Environment.NewLine);
                if (_isSpeechOn)
                    Monitor.Wait(asyncVoiceLock);
            }
            catch
            {
                Write("The SAPIBot parser encountered an error.");
            }
        }

        /// <summary>
        /// Types fast no speech.
        /// </summary>
        /// <param name="outputText">The output text.</param>
        public void TypeFastNoSpeech(string outputText)
        {
            if (null == Write)
                return;

            try
            {
                Write(Name + ": " + outputText + Environment.NewLine + Environment.NewLine);
            }
            catch
            {
                Write("The SAPIBot parser encountered an error.");
            }
        }

        /// <summary>
        /// Types text exactly as it is entered.
        /// </summary>
        /// <param name="outputText">The output text.</param>
        public void TypeVerbatim(string outputText)
        {

            if (null == Write)
                return;
            BotServerBase.DebugWriteToConsole("try lock asyncVoiceLock (verbatim)");
            lock (asyncVoiceLock)
            {
                BotServerBase.DebugWriteToConsole(" asyncVoiceLock locked (verbatim)");
                try
                {
                    for (int i = 0; i < outputText.Length; i++)
                    {
                        Write(outputText.Substring(i, 1));
                        Thread.Sleep(10);
                    }
                    Write(Environment.NewLine + Environment.NewLine);
                }
                catch
                {
                    Write("The SAPIBot parser encountered an error.");
                }
                BotServerBase.DebugWriteToConsole(" asyncVoiceLock released (verbatim)");
            }
        }

        #endregion

    }
}
